//
// Created by Administrator on 2020/3/20.
//

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
#include <stdlib.h>

//#include "SerialPort.h"

#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

static speed_t getBaudrate(jint baudrate)
{
    switch(baudrate) {
        case 0: return B0;
        case 50: return B50;
        case 75: return B75;
        case 110: return B110;
        case 134: return B134;
        case 150: return B150;
        case 200: return B200;
        case 300: return B300;
        case 600: return B600;
        case 1200: return B1200;
        case 1800: return B1800;
        case 2400: return B2400;
        case 4800: return B4800;
        case 9600: return B9600;
        case 19200: return B19200;
        case 38400: return B38400;
        case 57600: return B57600;
        case 115200: return B115200;
        case 230400: return B230400;
        case 460800: return B460800;
        case 500000: return B500000;
        case 576000: return B576000;
        case 921600: return B921600;
        case 1000000: return B1000000;
        case 1152000: return B1152000;
        case 1500000: return B1500000;
        case 2000000: return B2000000;
        case 2500000: return B2500000;
        case 3000000: return B3000000;
        case 3500000: return B3500000;
        case 4000000: return B4000000;
        default: return -1;
    }
}


extern "C"
JNIEXPORT jobject JNICALL
Java_com_sagittariusjin_android_serialportbecoroutinelib_SerialPort_open(JNIEnv *env, jclass type, jstring path_, jint baudrate,
                                                               jint flags) {
    int fd;
    speed_t speed;
    jobject mFileDescriptor;

    /* Check arguments */
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            /* TODO: throw an exception */
            LOGE("Invalid baudrate");
            return NULL;
        }
    }

    /* Opening device */
    {
        jboolean iscopy;
        const char *path_utf = env->GetStringUTFChars( path_, &iscopy);
        LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
        fd = open(path_utf, O_RDWR | flags);
        LOGD("open() fd = %d", fd);
        env->ReleaseStringUTFChars(path_, path_utf);
        if (fd == -1)
        {
            /* Throw an exception */
            LOGE("Cannot open port");
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Configure device */
    {
        struct termios cfg;
        LOGD("Configuring serial port");
        if (tcgetattr(fd, &cfg))
        {
            LOGE("tcgetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }

        cfmakeraw(&cfg);
        cfsetispeed(&cfg, speed);
        cfsetospeed(&cfg, speed);

        if (tcsetattr(fd, TCSANOW, &cfg))
        {
            LOGE("tcsetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Create a corresponding file descriptor */
    {
        jclass cFileDescriptor = env->FindClass( "java/io/FileDescriptor");
        jmethodID iFileDescriptor = env->GetMethodID( cFileDescriptor, "<init>", "()V");
        jfieldID descriptorID = env->GetFieldID( cFileDescriptor, "descriptor", "I");
        mFileDescriptor = env->NewObject( cFileDescriptor, iFileDescriptor);
        env->SetIntField( mFileDescriptor, descriptorID, (jint)fd);
    }
    LOGD("is good ");
    return mFileDescriptor;

}

extern "C"
JNIEXPORT void JNICALL
Java_com_sagittariusjin_android_serialportbecoroutinelib_SerialPort_close(JNIEnv *env, jobject thiz) {

    jclass SerialPortClass = env->GetObjectClass(thiz);
    jclass FileDescriptorClass = env->FindClass( "java/io/FileDescriptor");

    jfieldID mFdID = env->GetFieldID( SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
    jfieldID descriptorID = env->GetFieldID( FileDescriptorClass, "descriptor", "I");

    jobject mFd = env->GetObjectField( thiz, mFdID);
    jint descriptor = env->GetIntField( mFd, descriptorID);

    LOGD("close(fd = %d)", descriptor);
    close(descriptor);

}



#define POLYNOMIAL 0x8408
#define PRESET_VALUE 0xFFFF
#define CHECK_VALUE 0xF0B8
#define CALC_CRC 0x1
#define CHECK_CRC 0x0

//
unsigned *calc_crc(unsigned int byte_len, unsigned char *data_byte) {
    unsigned int current_crc_value;
    unsigned int i, j;
    current_crc_value = PRESET_VALUE;
    for (i = 0; i < byte_len; i++) {
        current_crc_value = current_crc_value ^ data_byte[i];
        for (j = 0; j < 8; j++) {
            if (current_crc_value & 0x0001) {
                current_crc_value = (current_crc_value >> 1) ^ POLYNOMIAL;
            } else {
                current_crc_value = (current_crc_value >> 1);
            }
        }
    }
    current_crc_value = ~current_crc_value;
    unsigned int hi = ((current_crc_value & 0xFF00) >> 8);  //高位置
    unsigned int lo = (current_crc_value & 0x00FF);
    unsigned int a[] = {lo, hi};
    unsigned int *pVoid = (unsigned int *) malloc(sizeof(a));
    *pVoid = a[0];
    *(pVoid + 1) = a[1];
    return pVoid;
}

//用于modbus的crc校验
unsigned char *crc16_MODBUS(unsigned char *ptr, unsigned int len) {
    unsigned long wcrc = 0XFFFF;//预置16位crc寄存器，初值全部为1
    unsigned char temp;//定义中间变量
    int i = 0, j = 0;//定义计数
    for (i = 0; i < len; i++)//循环计算每个数据
    {
        temp = *ptr & 0X00FF;//将八位数据与crc寄存器亦或
        ptr++;//指针地址增加，指向下个数据
        wcrc ^= temp;//将数据存入crc寄存器
        for (j = 0; j < 8; j++)//循环计算数据的
        {
            if (wcrc & 0X0001)//判断右移出的是不是1，如果是1则与多项式进行异或。
            {
                wcrc >>= 1;//先将数据右移一位
                wcrc ^= 0XA001;//与上面的多项式进行异或
            } else//如果不是1，则直接移出
            {
                wcrc >>= 1;//直接移出
            }
        }
    }
    temp = wcrc;//crc的值

    unsigned char lo = wcrc;//crc的低八位
    unsigned char hi = wcrc >> 8;//crc的高八位
    unsigned char result[] = {lo, hi};
    unsigned char *pVoid = (unsigned char *) malloc(sizeof(result));
    *pVoid = result[0];
    *(pVoid + 1) = result[1];

    return pVoid;

}
//com.sunvua.android.serialportsbecoroutine.utils
//com.sunvua.android.serialportsbecoroutine
extern "C"
JNIEXPORT jbyteArray  JNICALL
Java_com_sagittariusjin_android_serialportbecoroutinelib_NativeLib_utils_CRCStorytelling_crc16ModBus(JNIEnv *env, jclass thiz,
                                                                  jint byte_len, jbyteArray data_byte) {
    unsigned char *addArr = (unsigned char *) env->GetByteArrayElements( data_byte, 0);
    unsigned char *pInt = crc16_MODBUS(addArr, (unsigned int) byte_len);
    jbyteArray pVoid = env->NewByteArray( 2);
    jbyte buffer[2];
    buffer[0] = (jbyte) *pInt;
    buffer[1] = (jbyte) *(pInt + 1);

    env->SetByteArrayRegion( pVoid, 0, 2, buffer);
    if (pInt != NULL) {
        free(pInt);
        pInt = NULL;
    }
    env->ReleaseByteArrayElements(data_byte,reinterpret_cast<jbyte *>(addArr), 0);
    return pVoid;
}

extern "C"
JNIEXPORT jbyteArray JNICALL Java_com_sagittariusjin_android_serialportbecoroutinelib_NativeLib_utils_CRCStorytelling_crc16
        (JNIEnv *env, jclass thiz, jint byte_len, jbyteArray data_byte) {
//    int length = env->GetArrayLength(env, data_byte);
//    jbyte *addArr = env->GetByteArrayElements(env, data_byte, 0);
//    unsigned char *data = malloc(length);
//    for (int i = 0; i < length; i++) {
//        data[i] = (unsigned char) (*(addArr + i));
//    }
//
//
//    unsigned int crc = calc_crc(byte_len, data);
//    unsigned char *addArr = (unsigned char *) env->GetByteArrayElements(env, data_byte, 0);
//    unsigned int crc = calc_crc(byte_len, addArr);
//    jbyte *string = env->GetByteArrayElements(env, data_byte, 0);
    unsigned char *addArr = (unsigned char *) env->GetByteArrayElements( data_byte, 0);
    unsigned int *pInt = calc_crc((unsigned int) byte_len, addArr);
    jbyteArray pVoid = env->NewByteArray( 2);
    jbyte buffer[2];
    buffer[0] = (jbyte) *pInt;
    buffer[1] = (jbyte) *(pInt + 1);

    env->SetByteArrayRegion( pVoid, 0, 2, buffer);
    if (pInt != NULL) {
        free(pInt);
        pInt = NULL;
    }
    env->ReleaseByteArrayElements(data_byte, reinterpret_cast<jbyte *>(addArr), 0);
    return pVoid;
}
